package ar.com.cema.methodology.analyzer.services.business.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import ar.com.cema.methodology.analyzer.exceptions.WrongProjectConfigurationException;
import ar.com.cema.methodology.analyzer.model.Methodology;
import ar.com.cema.methodology.analyzer.model.MethodologyUsageStatistic;
import ar.com.cema.methodology.analyzer.model.Survey;
import ar.com.cema.methodology.analyzer.model.Variable;
import ar.com.cema.methodology.analyzer.services.beans.MethodologyUsageReport;
import ar.com.cema.methodology.analyzer.services.beans.ProjectConfiguration;
import ar.com.cema.methodology.analyzer.services.beans.reports.MethodologyScore;
import ar.com.cema.methodology.analyzer.services.business.MethodologyUsageBO;
import ar.com.cema.methodology.analyzer.services.repository.SimpleObjectRepository;
import ar.com.cema.methodology.analyzer.services.repository.StatisticsRepository;

@Repository
public class MethodologyUsageBOImpl implements MethodologyUsageBO {

	@Autowired
	private SimpleObjectRepository simpleObjectRepository;
	
	@Autowired
	private StatisticsRepository statisticsRepository;
	
	private Log logger = LogFactory.getLog(MethodologyUsageBOImpl.class);
	
	
	public void setSimpleObjectRepository(SimpleObjectRepository simpleObjectRepository) {
		this.simpleObjectRepository = simpleObjectRepository;
	}
	public void setStatisticsRepository(StatisticsRepository statisticsRepository) {
		this.statisticsRepository = statisticsRepository;
	}

	
	@Override
	public Collection<Variable> listVariables() {
		return simpleObjectRepository.findAll(Variable.class);
	}

	@Override
	public MethodologyUsageReport generateReportFor(ProjectConfiguration projectConfiguration) {
		if ( projectConfiguration.exceedsMaximumInput() ) {
			throw new WrongProjectConfigurationException("Total user input cannot exceed 100%.");
		}
		
		MethodologyUsageReport methodologyUsageReport = new MethodologyUsageReport();
		List<MethodologyScore> methodologiesScores = getTopMethodologyFor(projectConfiguration);
		
		if ( methodologiesScores.size() > 0 ) {
			methodologyUsageReport.setBestMethodologyScore(methodologiesScores.get(0));
			methodologyUsageReport.setWorstMethodologyScore(methodologiesScores.get(methodologiesScores.size() -1));
		}
		
		methodologyUsageReport.setMethodologiesScore(methodologiesScores);
		methodologyUsageReport.setSurveyCount(getTotalSurveyCount());
		return methodologyUsageReport;
	}
	
	private List<MethodologyScore> getTopMethodologyFor(ProjectConfiguration projectConfiguration) {
		Map<Variable, Integer> userInput = projectConfiguration.getUserConfiguration();
		Collection<Variable> variables = projectConfiguration.getUserConfiguration().keySet();
		Set<MethodologyScore> methodologiesScores = new TreeSet<MethodologyScore>();
		Collection<MethodologyUsageStatistic> methodologyUsageStatistics = statisticsRepository.findStatisticsOrderyByScoreFor(variables);

		logger.debug("\n\n#####################################################################################################################\n");
		
		for (MethodologyUsageStatistic statistic: methodologyUsageStatistics) {
			Integer userInputForVariable = userInput.get(statistic.getVariable());	
			Double newScore = (userInputForVariable.doubleValue()/100) * statistic.getScore().doubleValue();
			Methodology currentMethodology = statistic.getMethodology();
			
			logger.debug("Current statistic is for variable[id=" + statistic.getVariable().getId() + "], methodology[id=" + statistic.getMethodology().getId() + "]");
			logger.debug("User Input for variable[id=" + statistic.getVariable().getId() + "] is " + userInputForVariable);
			logger.debug("Statistic score is " + statistic.getScore());
			logger.debug("Score to add is " + newScore);
			
			if (methodologiesScores.contains(new MethodologyScore(currentMethodology, 0.0))) {
				MethodologyScore currentScore = get(methodologiesScores, currentMethodology);
				logger.debug("Old score is " + currentScore.getScore());
				
				currentScore.setScore(currentScore.getScore() + newScore);
				logger.debug("New score is " + currentScore.getScore());
				
				methodologiesScores.add(currentScore);
			} else {
				logger.debug("Methodology score not found, new object added to the list");
				methodologiesScores.add(new MethodologyScore(currentMethodology, newScore));
			}
		}
		
		List<MethodologyScore> list = new ArrayList<MethodologyScore>();
		list.addAll(methodologiesScores);
		Collections.sort(list, new Comparator<MethodologyScore>() {
			@Override
			public int compare(MethodologyScore arg0, MethodologyScore arg1) {
				return arg1.getScore().compareTo(arg0.getScore());
			} 
		});
		
		logger.debug("\n#####################################################################################################################\n\n");
		return list;
	}
	
	private Integer getTotalSurveyCount() {
		return simpleObjectRepository.count(Survey.class);
	}
	
	
	private MethodologyScore get(Set<MethodologyScore> set, Methodology methodology) {
		for (MethodologyScore methodologyScore : set) {
			if ( methodologyScore.getMethodology().equals(methodology) ) {
				return methodologyScore;
			}
		}
		throw new RuntimeException();
	}
}